﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using BindableDataGrid.Data;
using SilveR.RService;

//TODO: excel converter service? Would have to be a seperate service and utilise compression to transport xl files
//TODO: Bind close btn and tabcontrol visibility to tab page no
//TODO: Back button not working
//TODO: P value adjustment
//TODO: RServer to store statistics (get variable names right, synchronise with IVS?)


namespace SilveR
{
    public partial class App : Application
    {
        public static readonly string[] tabooColumnCharList = { "+", "*", "~", "`", "," };
        public static readonly string[] tabooDataCharList = { "`", "," };

        public App()
        {
            this.Startup += this.Application_Startup;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();

            //load in the char conversion file asynchronousy
            ArgumentConverters.LoadCharConversionList();

            //get the reports folder location
            RServiceClient client = new RServiceClient();
            client.GetReportsFolderAsync();
            client.GetReportsFolderCompleted += new EventHandler<GetReportsFolderCompletedEventArgs>(client_GetReportsFolderCompleted);
        }

        private void client_GetReportsFolderCompleted(object sender, GetReportsFolderCompletedEventArgs e)
        {
            reportsBaseURL = e.Result;
        }

        private static string reportsBaseURL;
        public static string ReportsBaseURL
        {
            get { return reportsBaseURL; }
        }


        //hold a reference to the datapage so that we can access it throughout the application
        private DataPage dataPage;
        public DataPage DataPage
        {
            get { return dataPage; }
            set { dataPage = value; }
        }

        //hold a reference to the analysispage so that we can access it throughout the application
        private AnalysesPage analysesPage;
        public AnalysesPage AnalysesPage
        {
            get { return analysesPage; }
            set { analysesPage = value; }
        }

        //this method returns a list of available data as datahandlers (available throughout application)
        public ObservableCollection<DataHandler> DataHandlers
        {
            get
            {
                ObservableCollection<DataHandler> dataHandlers = new ObservableCollection<DataHandler>();

                //if returning to the application then datapage will not have initialised (so will be null, so just return the empty datahandlers)
                if (dataPage == null) return dataHandlers;

                foreach (TabItem tab in dataPage.tabControlData.Items)
                {
                    GridView gv = (GridView)tab.Content;
                    dataHandlers.Add(gv.DataHandler);
                }

                return dataHandlers;
            }
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            this.RootVisual = new MainPage();
        }

        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            // If the app is running outside of the debugger then report the exception using
            // a ChildWindow control.
            if (!System.Diagnostics.Debugger.IsAttached)
            {
                // NOTE: This will allow the application to continue running after an exception has been thrown
                // but not handled. 
                // For production applications this error handling should be replaced with something that will 
                // report the error to the website and stop the application.
                e.Handled = true;
                ChildWindow errorWin = new ErrorWindow(e.ExceptionObject);
                
                errorWin.Show();

                string errorInfo = e.ExceptionObject.Message + Environment.NewLine;
                errorInfo = errorInfo + e.ExceptionObject.InnerException.Message + Environment.NewLine;
                errorInfo = errorInfo + e.ExceptionObject.StackTrace;

                RServiceClient client = new RServiceClient();
                client.ReportErrorAsync(errorInfo);
            }
        }
    }


    public static class ArgumentConverters
    {
        private static Dictionary<string, string> charConversionList;

        public static void LoadCharConversionList()
        {
            RServiceClient client = new RServiceClient();
            client.GetCharConversionListCompleted += new EventHandler<GetCharConversionListCompletedEventArgs>(client_GetCharConversionListCompleted);

            client.GetCharConversionListAsync();
        }

        private static void client_GetCharConversionListCompleted(object sender, GetCharConversionListCompletedEventArgs e)
        {
            charConversionList = new Dictionary<string, string>();

            foreach (string s in e.Result)
            {
                charConversionList.Add(s.Substring(0,1), s.Substring(2));
            }
        }

        public static string ConvertIllegalChars(string stringValue)
        {
            if (charConversionList == null) throw new NullReferenceException("Char conversion list not initialised");

            if (String.IsNullOrEmpty(stringValue)) return stringValue;

            foreach (KeyValuePair<string, string> kp in charConversionList)
            {
                if (stringValue.Contains(kp.Key))
                {
                    stringValue = stringValue.Replace(kp.Key, kp.Value);
                }
            }

            return stringValue;
        }

        public static string GetNULLOrText(string txt)
        {
            if (String.IsNullOrEmpty(txt))
            {
                txt = "NULL";
            }
            else
            {
                txt = "\"" + txt + "\"";
            }

            return txt;
        }

        public static string GetYesOrNo(bool value)
        {
            string val = value ? "Y" : "N";
            return val;
        }
    }


    public static class CollectionExtensions
    {
        public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> coll)
        {
            var c = new ObservableCollection<T>();
            foreach (var e in coll)
                c.Add(e);
            return c;
        }

        public static List<T> ToObservableCollection<T>(this ObservableCollection<T> coll)
        {
            var c = new List<T>();
            foreach (var e in coll)
                c.Add(e);
            return c;
        }
    }

    public static class DataTableExtensions
    {
        public static DataTable Copy(this DataTable dataTable)
        {
            //create a new datatable
            DataTable newDt = new DataTable("Copy");
            
            //add in the columns to the datatable
            foreach (DataColumn dc in dataTable.Columns)
            {
                newDt.Columns.Add(dc);
            }

            //now add in each row to the new datatable
            foreach (var r in dataTable.Rows)
            {
                DataRow newRow = new DataRow();

                foreach (var col in dataTable.Columns)
                {
                    string newValue = r[col.ColumnName].ToString().Trim();
                    newRow[col.ColumnName] = newValue;
                }

                newDt.Rows.Add(newRow);
            }

            return newDt;
        }

        public static void TrimAllDataInDataTable(this DataTable dataTable)
        {
            foreach (var c in dataTable.Columns)
            {
                foreach (var r in dataTable.Rows)
                {
                    string newValue = r[c.ColumnName].ToString().Trim();
                    r[c.ColumnName] = newValue;
                }
            }
        }

        public static void TransformColumn(this DataTable data, string column, string transformation)
        {
            if (transformation == "Rank")
            {
                List<double> colData = new List<double>(); // used to hold the values from the column so they can be sorted

                foreach (DataRow r in data.Rows)
                {
                    double val; //get the value
                    bool valOK = Double.TryParse(r[column].ToString(), out val);

                    if (valOK) //then it is numeric 
                    {
                        colData.Add(val);
                    }
                }

                //sort the data...
                colData.Sort();

                //go through and give basic ranks, adding to the ranking list (which contains keyvalue pairs for storing the rank and the original value)
                List<KeyValuePair<int, double>> rankingList = new List<KeyValuePair<int, double>>();
                int rank = 1;
                foreach (double v in colData)
                {
                    rankingList.Add(new KeyValuePair<int, double>(rank, v)); //add in the rank along with the value 

                    rank++;
                }

                //create another list which contains average ranks
                List<KeyValuePair<double, double>> averageRankingList = new List<KeyValuePair<double, double>>();
                foreach (KeyValuePair<int, double> kp in rankingList)
                {
                    var sameValuesRanks = rankingList.Where(v => v.Value == kp.Value); //find all the numbers with the same value (might only be one!), and copy into iEnumerable...
                    double averageRank = sameValuesRanks.Average(v => v.Key); //calculate the average rank... (will be same rank if only 1 value!)
                    averageRankingList.Add(new KeyValuePair<double, double>(averageRank, kp.Value)); //...add into the new list
                }

                //go through each row in dataset replacing the original value with the rank
                foreach (DataRow r in data.Rows)
                {
                    double val;
                    bool valOK = Double.TryParse(r[column].ToString(), out val);

                    if (valOK)
                    {
                        //find the item out of the average rank list that matches the value in the current row...
                        KeyValuePair<double, double> kp = averageRankingList.First(v => v.Value == val);
                        r[column] = kp.Key; //..and replace it with the rank
                    }
                }
            }
            else //all transformations other than Rank
            {
                foreach (DataRow r in data.Rows)
                {
                    double val; //get the value
                    bool valOK = Double.TryParse(r[column].ToString(), out val);

                    if (valOK && transformation != "None") //if reponse value is number (and actually doing a transformation)
                    {
                        switch (transformation) // do the selected transform
                        {
                            case "Log10":
                                if (val > 0)
                                    r[column] = Math.Log10(val);
                                else
                                    r[column] = null;

                                break;
                            case "Loge":
                                if (val > 0)
                                    r[column] = Math.Log(val);
                                else
                                    r[column] = null;

                                break;
                            case "Square Root":
                                if (val >= 0)
                                    r[column] = Math.Sqrt(val);
                                else
                                    r[column] = null;

                                break;
                            case "ArcSine":
                                if (val >= 0 && val <= 1)
                                    r[column] = Math.Asin(Math.Sqrt(val));
                                else
                                    r[column] = null;

                                break;
                        }
                    }
                }
            }
        }

        public static void RemoveBlankRow(this DataTable dtNew, string blankColumn)
        {
            if (!String.IsNullOrEmpty(blankColumn))
            {
                for (int i = dtNew.Rows.Count - 1; i >= 0; i--)
                {
                    DataRow theRow = dtNew.Rows[i];
                    
                    if (String.IsNullOrEmpty(theRow[blankColumn].ToString()))
                    {
                        dtNew.Rows.Remove(dtNew.Rows[i]);
                    }
                }
            }
        }
        
        public static bool CheckIsNumeric(this DataTable dtNew, string column)
        {
            if (!String.IsNullOrEmpty(column))
            {
                foreach (DataRow row in dtNew.Rows)
                {
                    double number;
                    bool isNumeric = Double.TryParse(row[column].ToString(), out number);

                    if (!isNumeric && !String.IsNullOrEmpty(row[column].ToString())) return false;
                }
            }
            return true;
        }


        public static string[] GetCSVArray(this DataTable dataTable)
        {
            List<string> lines = new List<string>(); //holds the output

            //get headers first...
            StringBuilder headerOut = new StringBuilder();
            bool firstHeader = true;

            foreach (DataColumn col in dataTable.Columns)
            {
                if (!firstHeader)
                {
                    headerOut = headerOut.Append(",");
                }

                headerOut = headerOut.Append(col.ColumnName);

                firstHeader = false;
            }

            //...and add it to the lines...
            lines.Add(headerOut.ToString());

            //...then go through each row and add each one to the lines...
            foreach (DataRow row in dataTable.Rows)
            {
                StringBuilder rowOut = new StringBuilder();

                bool firstColumn = true;

                foreach (DataColumn col in dataTable.Columns)
                {
                    if (!firstColumn)
                    {
                        rowOut = rowOut.Append(",");
                    }

                    string value = row[col.Caption].ToString();
                    rowOut = rowOut.Append(value);

                    firstColumn = false;
                }

                lines.Add(rowOut.ToString());
            }

            return lines.ToArray();
        }

        public static bool ColumnIsNumeric(this DataTable dataTable, string column)
        {
            if (!String.IsNullOrEmpty(column))
            {
                foreach (DataRow row in dataTable.Rows)
                {
                    double number;
                    bool isNumeric = Double.TryParse(row[column].ToString(), out number);

                    if (!isNumeric && !String.IsNullOrEmpty(row[column].ToString())) return false;
                }
            }
            return true;
        }
    }

    public class ListBoxItemEx : ListBoxItem
    {
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            e.Handled = false;
        }
    }
}